package com.lagou.server;

import com.lagou.encoder.JSONSerializer;
import com.lagou.encoder.RpcDecoder;
import com.lagou.handler.UserServerHandler;
import com.lagou.request.RpcRequest;
import com.lagou.zk.ServerInfoVO;
import com.lagou.zk.ZkHelper;
import io.netty.bootstrap.ServerBootstrap;
import io.netty.channel.ChannelInitializer;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.socket.SocketChannel;
import io.netty.channel.socket.nio.NioServerSocketChannel;
import io.netty.handler.codec.string.StringEncoder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;

/**
 * 服务端启动器
 */
@Slf4j
@Component
public class ServerStarter implements ApplicationRunner {
    private static ServerInfoVO serverInfo = null;

    //hostName:ip地址  port:端口号
    private static void startServer(String hostName, int port) throws InterruptedException {
        NioEventLoopGroup bossGroup = new NioEventLoopGroup();
        NioEventLoopGroup workerGroup = new NioEventLoopGroup();

        io.netty.bootstrap.ServerBootstrap serverBootstrap = new ServerBootstrap();
        serverBootstrap.group(bossGroup, workerGroup)
                .channel(NioServerSocketChannel.class)
                .childHandler(new ChannelInitializer<SocketChannel>() {
                    protected void initChannel(SocketChannel ch) throws Exception {
                        ChannelPipeline pipeline = ch.pipeline();
                        // 绑定出参为String格式
                        pipeline.addLast(new StringEncoder());
                        // 绑定入参为RpcRequest格式
                        pipeline.addLast(new RpcDecoder<>(RpcRequest.class, new JSONSerializer()));
                        pipeline.addLast(new UserServerHandler());
                    }
                });
        serverBootstrap.bind(hostName, port).sync();
    }

    /**
     * 上下文初始化完成后启动服务
     *
     * @param args
     * @throws Exception
     */
    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 注册服务，获取分配的端口
        serverInfo = ZkHelper.regServer("127.0.0.1");
        // 使用分配的端口，启用服务监听
        startServer("127.0.0.1", serverInfo.getPort());
        // 标识服务为可用
        ZkHelper.serverUp(serverInfo);
        log.info(String.format("服务端[%s]监听[%s:%d]开始", serverInfo.getServerName(), serverInfo.getIp(), serverInfo.getPort()));
    }

    /**
     * 上下文关闭时，主动设置服务状态为不可用
     *
     * @throws Exception
     */
    @PostConstruct
    public void onDestroy() throws Exception {
        if (serverInfo != null) {
            ZkHelper.serverDown(serverInfo);
        }
    }
}
